﻿using System;
using System.Globalization;
using System.Web.UI;
using System.Web.UI.WebControls.WebParts;
using System.Configuration;
using System.Web.Configuration;

namespace ErikLieben.SharePoint.Generic
{
    public abstract class BaseWebPart : WebPart
    {
        #region [ Properties ]

        /// <summary>
        /// Gets or sets the template path.
        /// </summary>
        /// <value>The template path.</value>
        public String TemplatePath
        {
            // REVIEW: get & set this in a hidden field?
            get { return ViewState["TemplatePath"] as String; }
            set { ViewState["TemplatePath"] = value; }
        }

        #endregion

        #region [ Methods ]

        #region [ Abstract Methods ]
        protected abstract void WireUpControls();
        protected abstract string DeterminateView();
        protected abstract void WireUpSharePointControls(Control uiTemplate);
        #endregion

        #region [ Overrided Methods ]

        /// <summary>
        /// Searches the current naming container for a server control with the specified <paramref name="id"/> parameter.
        /// </summary>
        /// <param name="id">The identifier for the control to be found.</param>
        /// <returns>
        /// The specified control, or null if the specified control does not exist.
        /// </returns>
        public override Control FindControl(String id)
        {
            return (Controls != null && Controls.Count == 1) ? Controls[0].FindControl(id) : base.FindControl(id);
        }

        /// <summary>
        /// Called by the ASP.NET page framework to notify server controls that use composition-based implementation to create any child controls they contain in preparation for posting back or rendering.
        /// </summary>
        protected override void CreateChildControls()
        {
            if (ChildControlsCreated) return;

            try
            {
                // Load the view(control) of the webpart
                Controls.Clear();
                if (String.IsNullOrEmpty(TemplatePath)) TemplatePath = DeterminateView();
                Control template = Page.LoadControl(TemplatePath);
                template.ID = "template";

                // Wire up controls and add view to webpart
                WireUpSharePointControls(template);
                Controls.Add(template);
                WireUpControls();
            }
            catch (Exception ex)
            {
                Controls.Clear();

                // Generate error report
                String errorReport = String.Empty;
                if (ShowProductionModeErorrs()) errorReport += "<strong>ERROR IN WEBPART</strong>";
                else
                {
                    while (ex != null)
                    {
                        errorReport += "<strong>" + ex.GetType() + "</strong><br />";
                        errorReport += "Message: " + (ex.Message ?? String.Empty) + "<br />";
                        errorReport += "Stack Trace: <br />" + ex.StackTrace ?? String.Empty;
                        ex = ex.InnerException;
                        if (ex != null) errorReport += "<hr />";
                    }
                }

                // Add Control to output
                LiteralControl literal = new LiteralControl();
                literal.Text = errorReport;
                Controls.Add(literal);
            }
            base.CreateChildControls();
        }

        #endregion

        /// <summary>
        /// Finds the control.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="searchContainer">The search container.</param>
        /// <param name="id">The id.</param>
        /// <param name="control">The control.</param>
        /// <param name="requiredControl">if set to <c>true</c> [required control].</param>
        /// <returns></returns>
        public Boolean FindControl<T>(Control searchContainer, String id, out T control, Boolean requiredControl) where T : Control
        {
            // Find the control in this container or it's first child control
            control = searchContainer.FindControl(id) as T;
            if (control == null && searchContainer.Controls != null && searchContainer.Controls.Count == 1)
                control = searchContainer.Controls[0].FindControl(id) as T;

            if (requiredControl && control == null)
                throw new UnableToLoadViewControlException(GetType().ToString(), ID, typeof(T).ToString(), id);

            return (control != null);
        }

        /// <summary>
        /// Finds the control.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="id">The id.</param>
        /// <param name="control">The control.</param>
        /// <param name="requiredControl">if set to <c>true</c> [required control].</param>
        /// <returns></returns>
        public Boolean FindControl<T>(String id, out T control, Boolean requiredControl) where T : Control
        {
            return FindControl<T>(this, id, out control, requiredControl);
        }

        /// <summary>
        /// Determinates if customErrrors or Compilation==Debug is true.
        /// </summary>
        /// <returns></returns>
        private Boolean ShowProductionModeErorrs()
        {
            // get sections from web.config
            CustomErrorsSection customErrorSection = ConfigurationManager.GetSection("system.web/customErrors") as CustomErrorsSection;
            CompilationSection compilationSection = ConfigurationManager.GetSection("system.web/compilation") as CompilationSection;


            return
                (customErrorSection != null && customErrorSection.Mode == CustomErrorsMode.On) ||
                (compilationSection != null && compilationSection.Debug == false);
        }

        #endregion

    }

    /// <summary>
    /// Exception thrown if the view control could not be found.
    /// </summary>
    public class UnableToLoadViewControlException : Exception
    {
        public UnableToLoadViewControlException() : base() { }
        public UnableToLoadViewControlException(String message) : base(message) { }
        public UnableToLoadViewControlException(String message, Exception innerException) : base(message, innerException) { }

        public UnableToLoadViewControlException(String webPartType, String webPartId, String elementType, String elementId) : base(String.Format(CultureInfo.InvariantCulture, "Web part template for the {0} control named '{1}' is missing the required {2} control named '{3}'", webPartType, webPartId, elementType, elementId)) { }
    }
}
